package it.geosolutions.mapproducers;

import it.geosolutions.colorreduction.CustomPaletteBuilder;
import it.geosolutions.inversecolormap.InverseColorMapOp;

import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.KernelJAI;
import javax.media.jai.LookupTableJAI;
import javax.media.jai.RenderedOp;
import javax.media.jai.operator.BandMergeDescriptor;
import javax.media.jai.operator.ColorQuantizerDescriptor;
import javax.media.jai.operator.ErrorDiffusionDescriptor;
import javax.media.jai.widget.ScrollingImagePanel;
import javax.swing.JFrame;

import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;

import org.geotools.image.ImageWorker;

public class MapProducersTest extends TestCase {

	private static final int MEDIAN_CUT_STEPS = 500;

	private static final int NEU_QUANT_STEPS = 100;

	public MapProducersTest(String name) {
		super(name);
	}

	public static void main(String args[]) throws IOException {
		TestRunner.run(suite());
	}

	public static TestSuite suite() {
		final TestSuite ts = new TestSuite();
		// ts.addTest(new MapProducersTest("testCustomPaletteBuilder"));
		// ts.addTest(new MapProducersTest("testQuantizerOctTreeNN"));
		// ts.addTest(new MapProducersTest("testGIFPaletteBuilder"));
		// ts
		// .addTest(new MapProducersTest(
		// "testGIFNearestNeighborErrorDiffusion"));
		ts
				.addTest(new MapProducersTest(
						"testGIFNearestNeighborInverseColorMap"));

		// ts.addTest(new MapProducersTest("testQuantizerMedianCutNN"));
		// ts.addTest(new MapProducersTest("testQuantizerNeuQuantizationNN"));
		return ts;
	}

	/**
	 * This algorithm seems to be pretty fast and to give good quality. It uses
	 * the OctTree quantization algorithm which is known NOT to be the best one,
	 * but IMHO is not that bad.
	 * 
	 * Care must be taken when playing with the
	 * 
	 * @throws IOException
	 */
	public void testGIFPaletteBuilder() throws IOException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////

		long sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			RenderedImage originalImage = JAI.create("ImageRead", new File(
					"c:/wms.png"));
			// originalImage = TranslateDescriptor.create(originalImage,
			// new Float(100), new Float(100), new InterpolationNearest(),
			// null);
			long time = System.nanoTime();
			RenderedImage image;
			// /////////////////////////////////////////////////////////////////
			//
			// Use the CustomPaletteBuilder but first reduce the alpha band to a
			// bitmask
			// with zero for transparent and 255 for opaque.
			//
			// /////////////////////////////////////////////////////////////////
			if (originalImage.getColorModel().hasAlpha()) {

				final RenderedImage alpha = new ImageWorker(originalImage)
						.retainLastBand().multiplyConst(new double[] { 255.0 })
						.retainFirstBand().getRenderedImage();

				final int numBands = originalImage.getSampleModel()
						.getNumBands();
				originalImage = new ImageWorker(originalImage).retainBands(
						numBands - 1).getRenderedImage();

				final ImageLayout layout = new ImageLayout();
				if (numBands == 4) {
					layout.setColorModel(new ComponentColorModel(ColorSpace
							.getInstance(ColorSpace.CS_sRGB), true, false,
							Transparency.BITMASK, DataBuffer.TYPE_BYTE));
				} else {
					layout.setColorModel(new ComponentColorModel(ColorSpace
							.getInstance(ColorSpace.CS_GRAY), true, false,
							Transparency.BITMASK, DataBuffer.TYPE_BYTE));
				}
				image = BandMergeDescriptor.create(originalImage, alpha,
						new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout))
						.getNewRendering();
			} else
				image = originalImage;
			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			final CustomPaletteBuilder pb = new CustomPaletteBuilder(image, 7,
					2, 2);
			pb.buildPalette();
			image = pb.getIndexedImage();
			if (Boolean.getBoolean("vis"))
				visualize(image, "testGIFPaletteBuilder");
			assert image.getColorModel() instanceof IndexColorModel;
			sum += (System.nanoTime() - time);
		}

		// /////////////////////////////////////////////////////////////////
		//
		// Visualize
		//
		// /////////////////////////////////////////////////////////////////
		// ImageIO.write(image, "gif", new File("c:/roger/aaaaaaaaa43196.gif"));
		System.out.println("testGIFPaletteBuilder "
				+ (sum / Integer.getInteger("loopNum").intValue()) / 1.0E6);

	}

	/**
	 * This algorithm seems to be pretty fast and to give good quality. It uses
	 * the OctTree quantization algorithm which is known NOT to be the best one,
	 * but IMHO is not that bad.
	 * 
	 * Care must be taken when playing with the
	 * 
	 * @throws IOException
	 */
	public void testGIFNearestNeighborErrorDiffusion() throws IOException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////
		RenderedImage originalImage = ImageIO.read(new File("c:/roger/ex.jpg"));
		RenderedImage imageT = ImageIO.read(new File("c:/roger/ex.gif"));

		long sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			long time = System.nanoTime();
			// /////////////////////////////////////////////////////////////////
			//
			// Use the CustomPaletteBuilder but first reduce the alpha band to a
			// bitmask
			// with zero for transparent and 255 for opaque.
			//
			// /////////////////////////////////////////////////////////////////
			final ImageLayout layout = new ImageLayout();
			layout.setColorModel(imageT.getColorModel());

			IndexColorModel icm = ((IndexColorModel) imageT.getColorModel());
			final byte[] r = new byte[icm.getMapSize()];
			icm.getReds(r);
			final byte[] g = new byte[icm.getMapSize()];
			icm.getGreens(g);
			final byte[] b = new byte[icm.getMapSize()];
			icm.getBlues(b);
			// error diffusion
			final KernelJAI ditherMask = new KernelJAI(1, 1,
					new float[] { 1.0f });
			final LookupTableJAI colorMap = new LookupTableJAI(new byte[][] {
					r, g, b });
			RenderedOp image = ErrorDiffusionDescriptor.create(originalImage,
					colorMap, ditherMask, new RenderingHints(
							JAI.KEY_IMAGE_LAYOUT, layout));

			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			assert image.getColorModel() instanceof IndexColorModel;

			// /////////////////////////////////////////////////////////////////
			//
			// Visualize
			//
			// /////////////////////////////////////////////////////////////////
			image.getTiles();
			sum += (System.nanoTime() - time);

		}
		System.out.println("testGIFNearestNeighbor "
				+ (sum / Integer.getInteger("loopNum").intValue()) / 1.0E9);
	}

	public void testGIFNearestNeighborInverseColorMap() throws IOException,
			InterruptedException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////
		RenderedImage originalImage = JAI.create("ImageRead", new File(
				"c:/world.tif"));
		// ImageIO.read(new File("c:/ita.png"));
		assert originalImage.getColorModel() instanceof ComponentColorModel;
		IndexColorModel icm = (IndexColorModel) ImageIO.read(
				new File("c:/world.gif")).getColorModel();
		// new PALFileLoader("c:\\ny256.pal").getIndexColorModel();
		InverseColorMapOp eicm = new InverseColorMapOp(icm, 6, 1);
		long sum = 0;
		for (int i = 0; i < Integer.getInteger("num",
				Integer.getInteger("loopNum").intValue()).intValue(); i++) {
			long time = System.nanoTime();

			final BufferedImage dst = (BufferedImage) eicm
					.filterRenderedImage(originalImage);

			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			assert dst.getColorModel() instanceof IndexColorModel;

			// /////////////////////////////////////////////////////////////////
			//
			// Visualize
			//
			// /////////////////////////////////////////////////////////////////
			if (Boolean.getBoolean("vis"))
				visualize(dst, "testGIFNearestNeighborInverseColorMap");
			sum += (System.nanoTime() - time);

			
			ImageIO.write(dst, "gif", new File("c:\\sample\\"+System.nanoTime()+".gif"));
			System.out.println(i);
			Thread.sleep(1000);

		}
		System.out.println("testGIFNearestNeighborInverseColorMap "
				+ (sum / Integer.getInteger("num",
						Integer.getInteger("loopNum").intValue()).intValue())
				/ 1.0E6);
	}

	private void visualize(RenderedImage image, String title) {
		final JFrame f = new JFrame();
		f.setTitle(title);
		f.getContentPane().add(new ScrollingImagePanel(image, 300, 300));
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.pack();
		f.setVisible(true);
	}

	public void testCustomPaletteBuilder() throws IOException {
		JAI.getDefaultInstance().getTileScheduler().setParallelism(4);
		double sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			final RenderedImage originalImage = JAI.create("ImageRead",
					new File("c:/wms.png"));
			long first = System.nanoTime();
			// final ParameterBlockJAI pbjCR = new ParameterBlockJAI(
			// "ColorReduction");
			// pbjCR.setParameter("numColors", 255);
			// pbjCR.setParameter("alphaThreshold", 255);
			// pbjCR.setParameter("subsampleX", 2);
			// pbjCR.setParameter("subsampleY", 2);
			// pbjCR.addSource(originalImage);
			// final RenderedOp image = JAI.create("ColorReduction", pbjCR);
			CustomPaletteBuilder cpb = new CustomPaletteBuilder(originalImage);
			cpb.buildPalette();
			assert cpb.getIndexColorModel() instanceof IndexColorModel;

			if (Boolean.getBoolean("vis"))
				visualize(cpb.getIndexedImage(), "testCustomPaletteBuilder");
			else
				assert cpb.getIndexedImage() != null;
			sum += (System.nanoTime() - first);
		}
		System.out.println("testCustomPaletteBuilder (ms) "
				+ (sum / Integer.getInteger("loopNum").intValue()) / 1E6);
	}

	/**
	 * 
	 * @throws IOException
	 */
	public void testQuantizerOctTreeNN() throws IOException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////

		long sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			final RenderedImage originalImage = ImageIO.read(new File(
					"c:/world.200401.3x21600x21600.C1.tif"));
			long time = System.nanoTime();
			// 300 seems to be a good number
			final RenderedOp cqImage = ColorQuantizerDescriptor.create(
					originalImage, ColorQuantizerDescriptor.OCTTREE,
					new Integer(255), new Integer(300), null, new Integer(2),
					new Integer(2), null);

			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			assert cqImage.getColorModel() instanceof IndexColorModel;

			// /////////////////////////////////////////////////////////////////
			//
			// Visualize
			//
			// /////////////////////////////////////////////////////////////////
			if (Boolean.getBoolean("vis"))
				visualize(cqImage, "testQuantizerOctTreeNN");
			else
				assert cqImage.getTiles() != null;
			sum += (System.nanoTime() - time);

		}
		System.out.println("testQuantizerOctTreeNN (ms) "
				+ (sum / Integer.getInteger("loopNum").intValue()) / 1.0E6);
	}

	public void testQuantizerMedianCutNN() throws IOException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////

		long sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			final RenderedImage originalImage = ImageIO.read(new File(
					"c:/world.200401.3x21600x21600.C1.tif"));
			long time = System.nanoTime();
			// 300 seems to be a good number
			final RenderedOp cqImage = ColorQuantizerDescriptor.create(
					originalImage, ColorQuantizerDescriptor.MEDIANCUT,
					new Integer(255), new Integer(MEDIAN_CUT_STEPS), null,
					new Integer(2), new Integer(2), null);

			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			assert cqImage.getColorModel() instanceof IndexColorModel;

			// /////////////////////////////////////////////////////////////////
			//
			// Visualize
			//
			// /////////////////////////////////////////////////////////////////
			if (Boolean.getBoolean("vis"))
				visualize(cqImage, "testQuantizerOctTreeNN");
			else
				assert cqImage.getTiles() != null;
			sum += (System.nanoTime() - time);

		}
		System.out.println("testQuantizerMedianCutNN steps(" + MEDIAN_CUT_STEPS
				+ ") (ms) " + (sum / Integer.getInteger("loopNum").intValue())
				/ 1.0E6);
	}

	/**
	 * 
	 * @throws IOException
	 */
	public void testQuantizerNeuQuantizationNN() throws IOException {
		// /////////////////////////////////////////////////////////////////
		//
		// Load test image, 4 bands, alpha translucent
		//
		// /////////////////////////////////////////////////////////////////

		long sum = 0;
		for (int i = 0; i < Integer.getInteger("loopNum").intValue(); i++) {
			final RenderedImage originalImage = ImageIO.read(new File(
					"c:/gaarc_subset.tif"));
			long time = System.nanoTime();
			// 300 seems to be a good number
			final RenderedOp cqImage = ColorQuantizerDescriptor.create(
					originalImage, ColorQuantizerDescriptor.NEUQUANT,
					new Integer(255), new Integer(NEU_QUANT_STEPS), null,
					new Integer(4), new Integer(4), null);

			// /////////////////////////////////////////////////////////////////
			//
			// Now the magic
			//
			// /////////////////////////////////////////////////////////////////
			assert cqImage.getColorModel() instanceof IndexColorModel;

			// /////////////////////////////////////////////////////////////////
			//
			// Visualize
			//
			// /////////////////////////////////////////////////////////////////
			if (Boolean.getBoolean("vis"))
				visualize(cqImage, "testQuantizerNeuQuantizationNN");
			else
				assert cqImage.getTiles() != null;
			sum += (System.nanoTime() - time);

		}
		System.out.println("testQuantizerNeuQuantizationNN (ms) "
				+ (sum / Integer.getInteger("loopNum").intValue()) / 1.0E6);
	}
}